home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Overload Trio 2
/
Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO
/
dir30
/
alsps.zip
/
ALSP7.DOC
< prev
next >
Wrap
Lisp/Scheme
|
1993-11-04
|
12KB
|
365 lines
This is Lesson 7 of a series of AutoLISP training exercises given
on the CompuServe ADESK Forum by the Autodesk, Inc. Training
Department.
WRITING NEW AUTOCAD COMMANDS (Part Three)
A DESIGN FOR A NEW AUTOCAD COMMAND
In the preceding tutorials, you have learned about function
definitions, creating new AutoCAD commands, required arguments to
functions, and global and local variables.
It's now time to use what you have learned (along with the topics
we discuss in this tutorial) to write a new AutoCAD command.
There are some general design parameters that will apply to all
new AutoCAD commands you will write in AutoLISP.
1. Mimic the AutoCAD command prompt interface.
2. Leave the drawing environment as you found it.
3. Avoid conflict with other applications.
The command you will write in this tutorial will prompt the user
for two opposing corners of a rectangle, and draw a rectangular,
closed, 2d polyline.
THE USER INTERFACE
AutoLISP-based commands should mimic existing AutoCAD command
prompt sequences as much as possible. The user will find an
AutoCAD-like command easier to work with than one that invents
its own interface, if only because the prompt sequence is
internally consistent with the interface the user already knows.
First, the command BOX is defined. There are no required arguments
to the function, nor are any local variables declared; at least,
not until the function is fully debugged.
Open a new text file named BOX.LSP and begin writing your new
command.
(defun C:BOX ()
)
Next, a prompt is generated for the user to select a corner
point. The prompt is derived from the ZOOM Window command
prompt, which also prompts the user for two opposing corners of a
rectangle.
(defun C:BOX ()
(setq pt1 (getpoint "\nFirst corner: "))
)
This appears to be a good time to visually show the user the
rectangle that would be drawn were a second point to be chosen.
The GETPOINT function doesn't quite handle this case, so now
would be a good time to open up the APR and browse through the
function listings to see if an existing function does what you
need. It's always better to check first and see if the wheel
exists before you run off to re-invent one.
Go ahead. We'll still be here when you finish reading.
Did you find the listing in section 4.51 for GETCORNER?
As you can see, GETCORNER takes a base point argument. In our
function, that will be the first corner point chosen by the user.
After prompting the user for the other corner, GETCORNER will
visually drag a rectangle to the other corner point and return
that point when chosen by the user.
(defun C:BOX ()
(setq pt1 (getpoint "\nFirst corner: "))
(setq pt2 (getcorner pt1 "\nOther corner: "))
)
Try the new BOX command in its current state to test the user
interface.
Command: (load "box")
C:BOX
Command: BOX
First corner: <pick>
Other corner: <drag and pick>
Aren't you glad you took the time to browse through the APR to
see if an existing function handled window dragging? What does
that tell you about the APR? How often should you refer to it
when you first start writing AutoLISP programs and need specific
functionality from a routine?
The interface is now working. All that remains is to draw the
polyline based on the values stored in PT1 and PT2.
The COMMAND function is called to begin drawing the polyline.
The command name is prefaced with a period to ensure that the
native PLINE command is accessed in case it has been UNDEFINEd in
the current drawing editor session (see the UNDEFINE command in
the AutoCAD Reference Manual). PT1 is used as an argument to the
"From point:" prompt of the PLINE command.
(defun C:BOX ()
(setq pt1 (getpoint "\nFirst corner: "))
(setq pt2 (getcorner pt1 "\nOther corner: "))
(command ".PLINE"
pt1
)
)
This begins the polyline at PT1. The next step in the command
algorithm will be to calculate the other two corner points based
on PT1 and PT2, and send the points in the appropriate order to
the PLINE command.
WORKING WITH POINTS AS LISTS OF REAL NUMBERS
AutoLISP stores a point coordinate as a list of three real
numbers. For example, this expression binds the variable PT to
the list (1.0 2.0 3.0).
Command: (setq pt (getpoint "\nPoint: "))
Point: 1,2,3
(1.0 2.0 3.0)
Note that a 3d point need not necessarily be thought of as one
thing. It's true that the list storing the coordinate values is
a single object, but it is a complex object, itself consisting of
a list of three elements. Thinking about it in this way, it's
easy to conceive of a point as separate X, Y, and Z values
combined together in some arbitrary but very useful association.
AutoLISP has many functions to retrieve and replace elements (in
this case, separate coordinate values) within an existing list.
The most basic are the functions CAR and CDR (pronounced
"could-er", as in "I could have had a V8").
CAR and CDR each take one required argument, which should be a
list.
CAR returns the first element of a list.
CDR returns the list and everything in it EXCEPT for the first
element.
If the function CAR was applied to the variable PT, what value
would it return? Which coordinate value would that equate to?
Command: (car pt)
1.0
CAR returns the first element of PT, which is its X coordinate
value.
What would the function CDR return when applied to PT?
Command: (cdr pt)
(2.0 3.0)
At first glance, this doesn't appear particularly useful. But
what would happen if CAR was applied to the result above? What
coordinate value would the combination of CAR and CDR return?
Command: (car (cdr pt))
2.0
CDR returns the list after the first element is removed, so it
returns the list (2.0 3.0). When CAR receives this list as its
argument, it returns the first element, which is the value of the
Y coordinate of the point.
(car <point>) returns the X value of a point.
(car (cdr <point>)) returns the Y value of a point.
And, yes, (car (cdr (cdr <point>))) returns the Z value of a
point.
CALCULATING THE OTHER CORNERS
If PT1 and PT2 hold the values of two points that form opposing
corners of a rectangle, then the other corner points can be
determined by combining the X and Y values of PT1 with the Y and
X values of PT2. In our case, we ignore the Z values because
we're dealing with a 2d polyline.
PT3 shares the X value with PT1 and the Y value with PT2.
PT4 shares the X value with PT2 and the Y value with PT1.
PT3--------------------------------PT2
| |
| |
| |
| |
PT1--------------------------------PT4
How might you use the CAR and CDR functions to extract the
appropriate values from PT1 and PT2?
The X value of PT1 is: (car pt1)
The Y value of PT1 is: (car (cdr pt1))
The X value of PT2 is: (car pt2)
The Y value of PT2 is: (car (cdr pt2))
CREATING NEW POINT LISTS
If you understand how to retrieve the individual coordinate values
from PT1 and PT2, you might be wondering how to combine the
individual values into new lists that AutoLISP will treat as
points.
The LIST function takes an arbitrary number of arguments and
returns a list whose elements consist of the values of its
arguments.
For example, this expression returns a list of three elements.
Command: (list 1.0 2.0 3.0)
(1.0 2.0 3.0)
LIST returns the values of its arguments.
Command: (setq x 1.0)
1.0
!x
1.0
Command: (setq y 2.0)
2.0
!y
2.0
Command: (setq z 3.0)
3.0
!z
3.0
Command: (list x y z)
(1.0 2.0 3.0)
Command: (setq pt (list x y z))
(1.0 2.0 3.0)
!pt
(1.0 2.0 3.0)
Additional code is added to the function definition for BOX that
calculates the other corner points. The "C" option closes the
polyline and completes the PLINE command.
(defun C:BOX ()
(setq pt1 (getpoint "\nFirst corner: "))
(setq pt2 (getcorner pt1 "\nOther corner: "))
(command ".PLINE"
pt1
(list (car pt2) (car (cdr pt1)))
pt2
(list (car pt1) (car (cdr pt2)))
"C"
)
)
Test the current definition of BOX in the drawing editor.
Remember, AutoLISP isn't aware of changes you make to source
files until you load the file after the change has been made.
Command: (load "box")
C:BOX
Command: BOX
First corner: <pick>
Other corner: <drag and pick>
From point:
Current line-width is 0.0000
Arc/Close/Halfwidth/Length/Undo/Width/<Endpoint of line>:
Arc/Close/Halfwidth/Length/Undo/Width/<Endpoint of line>:
Arc/Close/Halfwidth/Length/Undo/Width/<Endpoint of line>:
Arc/Close/Halfwidth/Length/Undo/Width/<Endpoint of line>: C
Command: nil
BOX should have drawn a closed, rectangular 2d polyline bounded
by the opposing corners you chose. If it didn't do this, debug
the code and fix the problem before you continue.
CLEANING UP THE INTERFACE
The data acquisition part of the BOX interface works well, but
there are a couple of things you can do to clean up the overall
operation of the command.
1. There's no particular reason to echo the prompts from the
PLINE command to the command prompt area; in fact, it slows
things down.
2. The nil returned by BOX should be suppressed.
3. There's no reason to draw blips at the corners of the
polyline.
The GETVAR and SETVAR functions are used, along with SETQ, to
store the values of the system variables CMDECHO and BLIPMODE as
C:BOX is entered, change the values during the execution of
C:BOX, and restore the original values prior to exiting C:BOX.
PRIN1 is called as the last expression in the body of C:BOX to
print a null string to the terminal as C:BOX exits, suppressing
the nil that would otherwise be returned.
(defun C:BOX ()
(setq old_cmdecho (getvar "CMDECHO")
old_blipmode (getvar "BLIPMODE")
)
(setvar "CMDECHO" 0)
(setvar "BLIPMODE" 0)
(setq pt1 (getpoint "\nFirst corner: "))
(setq pt2 (getcorner pt1 "\nOther corner: "))
(command ".PLINE"
pt1
(list (car pt2) (car (cdr pt1)))
pt2
(list (car pt1) (car (cdr pt2)))
"C"
)
(setvar "CMDECHO" old_cmdecho)
(setvar "BLIPMODE" old_blipmode)
(prin1)
)
Test BOX. If everything works as expected, define the variables
used by the routine to be local to the function.
(defun C:BOX (/ pt1 pt2 old_cmdecho old_blipmode)
...
Congratulations. Now let your imagination run wild and think of
all the different ways you can use AutoLISP to customize the
AutoCAD command structure, and the new commands you can write to
help make your design tasks easier!
That's your assignment for this week!
Next week: Predicates, Logical Operators, and Conditional
Expressions, Part One